package work.timejay.utils;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.commons.io.IOUtils;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @className:CommonHttpUtils.java
 * @author:王文杰
 * @version:1.0.0
 * @description:TODO
 * @createTime 2023年12月08日 17:22:00
 */
@Slf4j
public class CommonHttpUtils {
    public static void main(String[] args) throws IOException, NoSuchAlgorithmException {
        //第一种apache ssl
        HttpEntity httpEntity = HttpUtils.get("https://www.ad2la.com/json?sortid=1&page=10");
        StringWriter stringWriter = new StringWriter();
        IOUtils.copy(httpEntity.getContent(), stringWriter);
        String resp = stringWriter.toString();
        JSONArray jsonArray = JSONUtil.parseArray(resp);

        //第二种stream方式
        streamSsl("11");

        //第三种解决ssl
        apacheTwo();
    }

    /**
     * okhttp3 解决ssl
     */
    public static String okhttp3(String url) {
        log.info("okhttp3 request url ----->{}", url);
        OkHttpClient client = new OkHttpClient().newBuilder()
                .connectTimeout(180, TimeUnit.SECONDS)
                .readTimeout(180, TimeUnit.SECONDS)
                .writeTimeout(180, TimeUnit.SECONDS)
                .build();

        Request request = new Request.Builder()
                .url(url)
                .get()
                .build();


        Response response;
        String result;

        try {
            response = client.newCall(request).execute();
            result = response.body().string();
            //反序列化
            System.out.println("result = " + result);
            return result;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 基于apache 解决ssl
     */
    public static void apacheTwo() throws NoSuchAlgorithmException, IOException {
        SSLContext sslContext = SSLContext.getDefault();

        SSLConnectionSocketFactory sslConnectionFactory = new SSLConnectionSocketFactory(
                sslContext,
                new String[]{"TLSv1.2"},
                null,
                NoopHostnameVerifier.INSTANCE);

        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("https", sslConnectionFactory)
                .register("http", PlainConnectionSocketFactory.INSTANCE)
                .build();

        HttpClientConnectionManager ccm = new BasicHttpClientConnectionManager(registry);
        HttpClient build = HttpClientBuilder.create()
                .setSSLSocketFactory(sslConnectionFactory)
                .setConnectionManager(ccm)
                .build();
        HttpGet httpGet = new HttpGet("https://www.ad2la.com/json?sortid=1&page=10");
        HttpResponse execute = build.execute(httpGet);
        String s = EntityUtils.toString(execute.getEntity());
        System.out.println("s = " + s);
    }


    /**
     * 基于stream 解决ssl
     *
     * @throws IOException
     */
    public static void streamSsl(String tagUrl) throws IOException {
        // 创建 URL 对象
        URL url = new URL("https://www.ad2la.com/json?sortid=1&page=10");
        // 创建连接
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        // 设置请求方法为 POST
        connection.setRequestMethod("GET");
        // 设置请求头部信息
        connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3823.400 QQBrowser/10.7.4307.400");
        connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        //connection.setRequestProperty("Content-Length", String.valueOf(urlParameters.length()));
        Map<String, String> map = new HashMap<>();
        map.forEach(connection::setRequestProperty);

        // 允许输入输出流
        connection.setDoInput(true);
        connection.setDoOutput(true);
        // 发送 POST 请求
        OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
        //writer.write(urlParameters);
        writer.flush();
        // 获取响应
        StringBuilder response = new StringBuilder();
        int responseCode = connection.getResponseCode();
        if (responseCode == HttpURLConnection.HTTP_OK) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            reader.close();
        } else {
            System.out.println("请求失败，响应码：" + responseCode);
        }
        // 关闭连接
        connection.disconnect();
        System.out.println("response.toString() = " + response.toString());
    }

    /**
     * 基于 apache http 解决ssl问题
     *
     * @author wwj
     * @since
     **/
    public static class HttpUtils {
        private static final Logger LOGGER = LoggerFactory.getLogger(HttpUtils.class);

        private static CloseableHttpClient httpClient = HttpClients.createDefault();
        private static CloseableHttpClient noopSslHttpClient = buildNoSSLAndRetryHandlerHttpClient();

        public static HttpEntity get(String url) throws IOException {
            HttpGet httpGet = new HttpGet(url);
            CloseableHttpResponse response = httpClient.execute(httpGet);
            HttpEntity entity = response.getEntity();

            return entity;
        }

        public static HttpEntity post(String url, String body, ContentType contentType, boolean withoutSslVerify) throws IOException {
            HttpPost httpPost = new HttpPost(url);
            HttpEntity stringEntity = new StringEntity(body, contentType);
            httpPost.setEntity(stringEntity);
            final CloseableHttpClient client = withoutSslVerify ? noopSslHttpClient : httpClient;
            CloseableHttpResponse response = client.execute(httpPost);
            return response.getEntity();
        }

        public static HttpEntity sslPost(String url, String body, ContentType contentType, SSLContext sslContext) throws IOException {
            SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
                    sslContext,
                    new String[]{"TLSv1"},
                    null,
                    new DefaultHostnameVerifier());

            BasicHttpClientConnectionManager connManager = new BasicHttpClientConnectionManager(
                    RegistryBuilder.<ConnectionSocketFactory>create()
                            .register("http", PlainConnectionSocketFactory.getSocketFactory())
                            .register("https", sslConnectionSocketFactory)
                            .build(),
                    null,
                    null,
                    null
            );
            HttpPost httpPost = new HttpPost(url);
            HttpEntity stringEntity = new StringEntity(body, contentType);
            httpPost.setEntity(stringEntity);
            HttpClient httpClient = HttpClientBuilder.create()
                    .setConnectionManager(connManager)
                    .build();
            HttpResponse response = httpClient.execute(httpPost);
            return response.getEntity();
        }

        public static HttpEntity postXml(String url, Map<String, String> params) throws IOException {
            return post(url, XmlUtils.mapToXml(params), ContentType.create(ContentType.APPLICATION_XML.getMimeType(), Consts.UTF_8), false);
        }

        public static HttpEntity postXmlWithoutSSlVerify(String url, Map<String, String> params) throws IOException {
            return post(url, XmlUtils.mapToXml(params), ContentType.create(ContentType.APPLICATION_XML.getMimeType(), Consts.UTF_8), true);
        }

        public static HttpEntity sslPostXml(String url, Map<String, String> params, SSLContext sslContext) throws IOException {
            return sslPost(url, XmlUtils.mapToXml(params), ContentType.create(ContentType.APPLICATION_XML.getMimeType(), Consts.UTF_8), sslContext);
        }

        private static CloseableHttpClient buildNoSSLAndRetryHandlerHttpClient() {
            return HttpClients.custom()
                    .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
                    .setRetryHandler(new HttpRequestRetryHandler() {
                        @Override
                        public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
                            if (executionCount > 3) {
                                LOGGER.warn("Maximum tries reached for client http pool", exception);
                                return false;
                            }
                            if (exception instanceof NoHttpResponseException) {
                                LOGGER.warn("No response from server on " + executionCount + " call");
                                return true;
                            }
                            return false;
                        }
                    }).build();
        }
    }

}
